package cn.iocoder.yudao.framework.common.util.algorithm.asymmetric;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;

import java.nio.charset.StandardCharsets;
import java.security.SignatureException;

@Slf4j
public class RSAHttpSignUtil {

    // 分配的应用appId
    public static String X_DD_APP_KEY = "X-DD-AppKey";

    // 请求签名
    public static String X_DD_SIGNATURE = "X-DD-Signature";

    // 时间戳毫秒值
    public static String X_DD_TIMESTAMP = "X-DD-Timestamp";

    // 请求唯一标识符，相同的值只会处理一次请求，长度在32个字符以内
    public static String X_DD_REQUEST_ID = "X-DD-RequestId";

    // 请求随机串，字符符合HTTP Header头部的规范，长度在32个字符以内，字符串组成为数字和字母
    public static String X_DD_NONCE = "X-DD-Nonce";

    public static HttpResponse post(String url, String requestBody, SignHeader header) {
        final HttpResponse httpResponse = HttpRequest.post(url)
                .body(requestBody, MediaType.APPLICATION_JSON_UTF8_VALUE)
                .header(X_DD_APP_KEY, header.getAppKey())
                .header(X_DD_TIMESTAMP, header.getTimestamp())
                .header(X_DD_REQUEST_ID, header.getRequestId())
                .header(X_DD_NONCE, header.getNonce())
                .header(X_DD_SIGNATURE, header.getSignature())
                .timeout(30 * 1000)
                .execute();
        return httpResponse;
    }

    public static String sign(SignHeader header, String requestBody, String privateKey) {
        final String timestamp = header.getTimestamp();
        final String requestId = header.getRequestId();
        final String nonce = header.getNonce();

        // 组合要验证的字符串
        final String dataString = timestamp + "\n" + requestId + "\n" + nonce + "\n" + requestBody;
        final byte[] data = dataString.getBytes(StandardCharsets.UTF_8);

        final byte[] signatureData = RSAUtils.signData(privateKey, data);
        final String signature = Base64.encode(signatureData);

        header.setSignature(signature);
        return signature;
    }

    public static boolean verifySign(SignHeader header, String requestBody, String publicKey) throws SignatureException {
        // 请实现验证签名的逻辑
        final String timestamp = header.getTimestamp();
        final String requestId = header.getRequestId();
        final String nonce = header.getNonce();

        // 组合要验证的字符串
        final String toBeVerified = timestamp + "\n" + requestId + "\n" + nonce + "\n" + requestBody;

        // 获取请求签名
        final String signature = header.getSignature();

        try {
            // 调用具体的签名验证逻辑，比如使用公钥验证签名等
            final byte[] data = toBeVerified.getBytes(StandardCharsets.UTF_8);

            final boolean verifyPassed = RSAUtils.verifySignature(publicKey, signature, data);

            if (verifyPassed) {
                log.info("签名验证-通过, appKey = {}", header.getAppKey());
            } else {
                log.warn("签名验证-不通过, publicKey = {}, signature = {}, toBeVerified = {}",
                        publicKey, signature, toBeVerified);
            }
            return verifyPassed;
        } catch (Exception e) {
            log.warn(String.format("签名验证-异常, publicKey = %s, signature = %s, toBeVerified = %s",
                    publicKey, signature, toBeVerified), e);
            throw new SignatureException(e.getMessage(), e);
        }
    }

    public static SignHeader createSignHeader(String appKey) {
        final SignHeader header = new SignHeader();
        header.setAppKey(appKey);
        header.setTimestamp(String.valueOf(System.currentTimeMillis()));
        header.setRequestId(IdUtil.fastSimpleUUID());
        header.setNonce(RandomUtil.randomString(32));
        return header;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class SignHeader {
        // 分配的appKey
        private String appKey;

        // 请求签名
        private String signature;

        // 标准UTC时间戳，单位毫秒
        private String timestamp;

        // 请求唯一标识符，相同的值只会处理一次请求，长度在32个字符以内
        private String requestId;

        // 请求随机串，字符符合HTTP Header头部的规范，长度在32个字符以内，字符串组成为数字和字母
        private String nonce;
    }
}
